home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Games Extra 1996 September
/
Amiga Games Extra CD-ROM 9-1996.iso
/
userbox
/
publicdomain
/
vim-4.2
/
src
/
normal.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-17
|
79KB
|
3,460 lines
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* Contains the main routine for processing characters in command mode.
* Communicates closely with the code in ops.c to handle the operators.
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
#undef EXTERN
#undef INIT
#define EXTERN
#define INIT(x) x
#include "ops.h"
/*
* Generally speaking, every command in normal() should either clear any
* pending operator (with clearop()), or set the motion type variable.
*/
/*
* If a count is given before the operator, it is saved in opnum.
*/
static linenr_t opnum = 0;
static linenr_t Prenum; /* The (optional) number before a command. */
static int prechar = NUL; /* prepended command char */
/*
* The visual area is remembered for reselection.
*/
static int resel_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
static linenr_t resel_VIsual_line_count; /* number of lines */
static colnr_t resel_VIsual_col; /* number of cols or end column */
#ifdef USE_MOUSE
static void find_start_of_word __ARGS((FPOS *));
static void find_end_of_word __ARGS((FPOS *));
static int get_mouse_class __ARGS((int));
#endif
static void prep_redo __ARGS((long, int, int, int, int));
static int checkclearop __ARGS((void));
static int checkclearopq __ARGS((void));
static void clearop __ARGS((void));
static void clearopbeep __ARGS((void));
static void del_from_showcmd __ARGS((int));
static void do_gd __ARGS((int nchar));
/*
* normal
*
* Execute a command in normal mode.
*
* This is basically a big switch with the cases arranged in rough categories
* in the following order:
*
* 0. Macros (q, @)
* 1. Screen positioning commands (^U, ^D, ^F, ^B, ^E, ^Y, z)
* 2. Control commands (:, <help>, ^L, ^G, ^^, ZZ, *, ^], ^T)
* 3. Cursor motions (G, H, M, L, l, K_RIGHT, , h, K_LEFT, ^H, k, K_UP,
* ^P, +, CR, LF, j, K_DOWN, ^N, _, |, B, b, W, w, E, e, $, ^, 0)
* 4. Searches (?, /, n, N, T, t, F, f, ,, ;, ], [, %, (, ), {, })
* 5. Edits (., u, K_UNDO, ^R, U, r, J, p, P, ^A, ^S)
* 6. Inserts (A, a, I, i, o, O, R)
* 7. Operators (~, d, c, y, >, <, !, =, Q)
* 8. Abbreviations (x, X, D, C, s, S, Y, &)
* 9. Marks (m, ', `, ^O, ^I)
* 10. Buffer setting (")
* 11. Visual (v, V, ^V)
* 12. Suspend (^Z)
* 13. Window commands (^W)
* 14. extended commands (starting with 'g')
* 15. mouse click
* 16. scrollbar movement
* 17. The end (ESC)
*/
void
normal()
{
register int c;
long n = 0; /* init for GCC */
int flag = FALSE;
int flag2 = FALSE;
int type = 0; /* type of operation */
int dir = FORWARD; /* search direction */
int nchar = NUL; /* next command char */
int finish_op;
linenr_t Prenum1;
char_u *searchbuff = NULL; /* buffer for search string */
FPOS *pos = NULL; /* init for gcc */
char_u *ptr = NULL;
int command_busy = FALSE;
int ctrl_w = FALSE; /* got CTRL-W command */
int old_col = 0;
int dont_adjust_op_end = FALSE;
Prenum = 0;
/*
* If there is an operator pending, then the command we take this time
* will terminate it. Finish_op tells us to finish the operation before
* returning this time (unless the operation was cancelled).
*/
finish_op = (op_type != NOP);
if (!finish_op && !yankbuffer)
opnum = 0;
State = NORMAL_BUSY;
c = vgetc();
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
if (c == NUL)
c = K_ZERO;
(void)add_to_showcmd(c, FALSE);
getcount:
/* Pick up any leading digits and compute 'Prenum' */
while ((c >= '1' && c <= '9') || (Prenum != 0 && (c == K_DEL || c == '0')))
{
if (c == K_DEL)
{
Prenum /= 10;
del_from_showcmd(4); /* delete the digit and ~@% */
}
else
Prenum = Prenum * 10 + (c - '0');
if (Prenum < 0) /* got too large! */
Prenum = 999999999;
c = vgetc();
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
(void)add_to_showcmd(c, FALSE);
}
/*
* If we got CTRL-W there may be a/another count
*/
if (c == Ctrl('W') && !ctrl_w && op_type == NOP)
{
ctrl_w = TRUE;
opnum = Prenum; /* remember first count */
Prenum = 0;
++no_mapping;
++allow_keys; /* no mapping for nchar, but keys */
c = vgetc(); /* get next character */
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
--no_mapping;
--allow_keys;
(void)add_to_showcmd(c, FALSE);
goto getcount; /* jump back */
}
/*
* If we're in the middle of an operator (including after entering a yank
* buffer with ") AND we had a count before the
* operator, then that count overrides the current value of Prenum. What
* this means effectively, is that commands like "3dw" get turned into
* "d3w" which makes things fall into place pretty neatly.
* If you give a count before AND after the operator, they are multiplied.
*/
if (opnum != 0)
{
if (Prenum)
Prenum *= opnum;
else
Prenum = opnum;
opnum = 0;
}
Prenum1 = (Prenum == 0 ? 1 : Prenum); /* Prenum often defaults to 1 */
/*
* Get an additional character if we need one.
* For CTRL-W we already got it when looking for a count.
*/
if (ctrl_w)
{
nchar = c;
c = Ctrl('W');
}
else if ((op_type == NOP && vim_strchr((char_u *)"@zm\"", c) != NULL) ||
(op_type == NOP && !VIsual_active &&
vim_strchr((char_u *)"rZ", c) != NULL) ||
vim_strchr((char_u *)"tTfF[]g'`", c) != NULL ||
(c == 'q' && !Recording && !Exec_reg))
{
++no_mapping;
++allow_keys; /* no mapping for nchar, but allow key codes */
nchar = vgetc();
#ifdef HAVE_LANGMAP
/* adjust chars > 127: tTfFr should leave lang of nchar unchanged! */
LANGMAP_ADJUST(nchar, vim_strchr((char_u *)"tTfFr", c) == NULL);
#endif
#ifdef RIGHTLEFT
if (p_hkmap && strchr("tTfFr", c) && KeyTyped) /* Hebrew mapped char */
nchar = hkmap(nchar);
#endif
--no_mapping;
--allow_keys;
(void)add_to_showcmd(nchar, FALSE);
}
if (p_sc)
flushbuf(); /* flush the showcmd characters onto the
* screen so we can see them while the command
* is being executed
*/
State = NORMAL;
if (nchar == ESC)
{
clearop();
goto normal_end;
}
msg_didout = FALSE; /* don't scroll screen up for normal command */
msg_col = 0;
#ifdef RIGHTLEFT
if (curwin->w_p_rl && KeyTyped) /* invert horizontal operations */
switch (c)
{
case 'l': c = 'h'; break;
case K_RIGHT: c = K_LEFT; break;
case 'h': c = 'l'; break;
case K_LEFT: c = K_RIGHT; break;
case '>': c = '<'; break;
case '<': c = '>'; break;
}
#endif
switch (c)
{
/*
* 0: Macros
*/
case 'q': /* (stop) recording into a named register */
if (checkclearop())
break;
/* command is ignored while executing a register */
if (!Exec_reg && do_record(nchar) == FAIL)
clearopbeep();
break;
case '@': /* execute a named buffer */
if (checkclearop())
break;
while (Prenum1--)
{
if (do_execbuf(nchar, FALSE, FALSE) == FAIL)
{
clearopbeep();
break;
}
}
break;
/*
* 1: Screen positioning commands
*/
case Ctrl('D'):
flag = TRUE;
case Ctrl('U'):
if ((c == Ctrl('U') && curwin->w_cursor.lnum == 1) ||
(c == Ctrl('D') && curwin->w_cursor.lnum ==
curbuf->b_ml.ml_line_count))
clearopbeep();
else
{
if (checkclearop())
break;
halfpage(flag, Prenum);
}
break;
case Ctrl('B'):
case K_S_UP:
case K_PAGEUP:
dir = BACKWARD;
case Ctrl('F'):
case K_S_DOWN:
case K_PAGEDOWN:
if (checkclearop())
break;
(void)onepage(dir, Prenum1);
break;
case Ctrl('E'):
if (checkclearop())
break;
scrollup(Prenum1);
if (p_so)
cursor_correct();
/* We may have moved to another line -- webb */
coladvance(curwin->w_curswant);
cursupdate();
updateScreen(VALID);
break;
case Ctrl('Y'):
if (checkclearop())
break;
scrolldown(Prenum1);
if (p_so)
cursor_correct();
/* We may have moved to another line -- webb */
coladvance(curwin->w_curswant);
updateScreen(VALID);
break;
case 'z':
if (checkclearop())
break;
if (nchar < 0x100 && isdigit(nchar))
{
Prenum = nchar - '0';
for (;;)
{
++no_mapping;
++allow_keys; /* no mapping for nchar, but allow key codes */
nchar = vgetc();
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
--no_mapping;
--allow_keys;
(void)add_to_showcmd(nchar, FALSE);
if (c == K_DEL)
Prenum /= 10;
else if (nchar < 0x100 && isdigit(nchar))
Prenum = Prenum * 10 + (nchar - '0');
else if (nchar == CR)
{
win_setheight((int)Prenum);
break;
}
else if (nchar == 'l' || nchar == 'h' ||
nchar == K_LEFT || nchar == K_RIGHT)
{
Prenum1 = Prenum ? Prenum : 1;
goto dozet;
}
else
{
clearopbeep();
break;
}
}
op_type = NOP;
break;
}
dozet:
/*
* If line number given, set cursor, except for "zh", "zl", "ze" and
* "zs"
*/
if (vim_strchr((char_u *)"hles", nchar) == NULL &&
nchar != K_LEFT && nchar != K_RIGHT &&
Prenum && Prenum != curwin->w_cursor.lnum)
{
setpcmark();
if (Prenum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
else
curwin->w_cursor.lnum = Prenum;
}
switch (nchar)
{
case NL: /* put curwin->w_cursor at top of screen */
case CR:
beginline(TRUE);
/* FALLTHROUGH */
case 't':
scroll_cursor_top(0, TRUE);
break;
case '.': /* put curwin->w_cursor in middle of screen */
beginline(TRUE);
/* FALLTHROUGH */
case 'z':
scroll_cursor_halfway(TRUE);
break;
case '-': /* put curwin->w_cursor at bottom of screen */
beginline(TRUE);
/* FALLTHROUGH */
case 'b':
scroll_cursor_bot(0, TRUE);
break;
/* "zh" - scroll screen to the right */
case 'h':
case K_LEFT:
if (!curwin->w_p_wrap)
{
colnr_t s, e;
if ((colnr_t)Prenum1 > curwin->w_leftcol)
curwin->w_leftcol = 0;
else
curwin->w_leftcol -= (colnr_t)Prenum1;
n = curwin->w_leftcol + Columns -
(curwin->w_p_nu ? 8 : 0) - 1;
if (curwin->w_virtcol > (colnr_t)n)
coladvance((colnr_t)n);
getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
if (e > (colnr_t)n)
coladvance(s - 1);
redraw_later(NOT_VALID);
}
break;
/* "zl" - scroll screen to the left */
case 'l':
case K_RIGHT:
if (!curwin->w_p_wrap)
{
colnr_t s, e;
/* scroll the window left */
curwin->w_leftcol += (colnr_t)Prenum1;
/* If the cursor has moved off the screen, put it at the
* first char on the screen */
if (curwin->w_leftcol > curwin->w_virtcol)
(void)coladvance(curwin->w_leftcol);
/* If the start of the character under the cursor is not
* on the screen, advance the cursor one more char. If
* this fails (last char of the line) adjust the
* scrolling. */
getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
if (s < curwin->w_leftcol)
if (coladvance(e + 1) == FAIL)
curwin->w_leftcol = s;
redraw_later(NOT_VALID);
}
break;
/* "zs" - scroll screen, cursor at the start */
case 's':
if (!curwin->w_p_wrap)
{
colnr_t s;
getvcol(curwin, &curwin->w_cursor, &s, NULL, NULL);
curwin->w_leftcol = s;
redraw_later(NOT_VALID);
}
break;
/* "ze" - scroll screen, cursor at the end */
case 'e':
if (!curwin->w_p_wrap)
{
colnr_t e;
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &e);
if ((long)e < Columns)
curwin->w_leftcol = 0;
else
curwin->w_leftcol = e - Columns + 1;
redraw_later(NOT_VALID);
}
break;
case Ctrl('S'): /* ignore CTRL-S and CTRL-Q to avoid problems */
case Ctrl('Q'): /* with terminals that use xon/xoff */
break;
default:
clearopbeep();
}
updateScreen(VALID);
break;
/*
* 2: Control commands
*/
case ':':
if (VIsual_active)
goto dooperator;
if (checkclearop())
break;
/*
* translate "count:" into ":.,.+(count - 1)"
*/
if (Prenum)
{
stuffReadbuff((char_u *)".");
if (Prenum > 1)
{
stuffReadbuff((char_u *)",.+");
stuffnumReadbuff((long)Prenum - 1L);
}
}
do_cmdline(NULL, FALSE, FALSE);
break;
case K_HELP:
case K_F1:
if (checkclearopq())
break;
do_help((char_u *)"");
break;
case Ctrl('L'):
if (checkclearop())
break;
updateScreen(CLEAR);
break;
case Ctrl('G'):
if (checkclearop())
break;
/* print full name if count given or :cd used */
fileinfo(did_cd | (int)Prenum, FALSE, FALSE);
/*
* In Visual mode and "^O^G" in Insert mode, the message will be
* overwritten by the mode message. Wait a bit, until a key is hit.
*/
if ((VIsual_active || (restart_edit && p_smd)) && KeyTyped)
{
setcursor();
flushbuf();
mch_delay(10000L, FALSE);
}
break;
case K_CCIRCM: /* CTRL-^, short for ":e #" */
if (checkclearopq())
break;
(void)buflist_getfile((int)Prenum, (linenr_t)0, GETF_SETMARK|GETF_ALT);
break;
case 'Z': /* write, if changed, and exit */
if (checkclearopq())
break;
if (nchar != 'Z')
{
clearopbeep();
break;
}
stuffReadbuff((char_u *)":x\n");
break;
case Ctrl(']'): /* :ta to current identifier */
case 'K': /* run program for current identifier */
if (VIsual_active) /* :ta to visual highlighted text */
{
if (VIsual.lnum != curwin->w_cursor.lnum)
{
clearopbeep();
break;
}
if (lt(curwin->w_cursor, VIsual))
{
ptr = ml_get_pos(&curwin->w_cursor);
n = VIsual.col - curwin->w_cursor.col + 1;
}
else
{
ptr = ml_get_pos(&VIsual);
n = curwin->w_cursor.col - VIsual.col + 1;
}
end_visual_mode();
++RedrawingDisabled;
update_curbuf(NOT_VALID); /* update the inversion later */
--RedrawingDisabled;
}
if (checkclearopq())
break;
/*FALLTHROUGH*/
case 163: /* the pound sign, '#' for English keyboards */
if (c == 163)
c = '#';
/*FALLTHROUGH*/
case '*': /* / to current identifier or string */
case '#': /* ? to current identifier or string */
search_word:
if (c == 'g')
type = nchar; /* "g*" or "g#" */
else
type = c;
if (ptr == NULL && (n = find_ident_under_cursor(&ptr, (type == '*' ||
type == '#') ? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
{
clearop();
break;
}
if (Prenum)
stuffnumReadbuff(Prenum);
switch (type)
{
case '*':
stuffReadbuff((char_u *)"/");
/* FALLTHROUGH */
case '#':
if (type == '#')
stuffReadbuff((char_u *)"?");
/*
* put cursor at start of word, makes search skip the word
* under the cursor
*/
curwin->w_cursor.col = ptr - ml_get_curline();
if (c != 'g' && iswordchar(*ptr))
stuffReadbuff((char_u *)"\\<");
no_smartcase = TRUE; /* don't use 'smartcase' now */
break;
case 'K':
if (*p_kp == NUL)
stuffReadbuff((char_u *)":he ");
else
{
stuffReadbuff((char_u *)":! ");
stuffReadbuff(p_kp);
stuffReadbuff((char_u *)" ");
}
break;
default:
if (curbuf->b_help)
stuffReadbuff((char_u *)":he ");
else
stuffReadbuff((char_u *)":ta ");
}
/*
* Now grab the chars in the identifier
*/
while (n--)
{
/* put a backslash before \ and some others */
if (*ptr == '\\' || (!(type == '*' || type == '#') &&
vim_strchr(escape_chars, *ptr) != NULL))
stuffcharReadbuff('\\');
/* don't interpret the characters as edit commands */
if (*ptr < ' ' || *ptr > '~')
stuffcharReadbuff(Ctrl('V'));
stuffcharReadbuff(*ptr++);
}
if (c != 'g' && (type == '*' || type == '#') && iswordchar(ptr[-1]))
stuffReadbuff((char_u *)"\\>");
stuffReadbuff((char_u *)"\n");
break;
case Ctrl('T'): /* backwards in tag stack */
if (checkclearopq())
break;
do_tag((char_u *)"", 2, (int)Prenum1);
break;
/*
* Cursor motions
*/
case 'G':
goto_line:
op_motion_type = MLINE;
setpcmark();
if (Prenum == 0 || Prenum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
else
curwin->w_cursor.lnum = Prenum;
beginline(MAYBE);
break;
case 'H':
case 'M':
if (c == 'M')
{
int used = 0;
for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
if ((used += plines(curwin->w_topline + n)) >=
(curwin->w_height - curwin->w_empty_rows + 1) / 2)
break;
if (n && used > curwin->w_height)
--n;
}
else
n = Prenum;
op_motion_type = MLINE;
setpcmark();
curwin->w_cursor.lnum = curwin->w_topline + n;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
cursor_correct(); /* correct for 'so' */
beginline(MAYBE);
break;
case 'L':
op_motion_type = MLINE;
setpcmark();
curwin->w_cursor.lnum = curwin->w_botline - 1;
if (Prenum >= curwin->w_cursor.lnum)
curwin->w_cursor.lnum = 1;
else
curwin->w_cursor.lnum -= Prenum;
cursor_correct(); /* correct for 'so' */
beginline(MAYBE);
break;
case 'l':
case K_RIGHT:
case ' ':
op_motion_type = MCHAR;
op_inclusive = FALSE;
n = Prenum1;
while (n--)
{
if (oneright() == FAIL)
{
/* space wraps to next line if 'whichwrap' bit 1 set */
/* 'l' wraps to next line if 'whichwrap' bit 2 set */
/* CURS_RIGHT wraps to next line if 'whichwrap' bit 3 set */
if (((c == ' ' && vim_strchr(p_ww, 's') != NULL) ||
(c == 'l' && vim_strchr(p_ww, 'l') != NULL) ||
(c == K_RIGHT && vim_strchr(p_ww, '>') != NULL)) &&
curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
{
/* When deleting we also count the NL as a character.
* Set op_inclusive when last char in the line is
* included, move to next line after that */
if ((op_type == DELETE || op_type == CHANGE) &&
!op_inclusive && !lineempty(curwin->w_cursor.lnum))
op_inclusive = TRUE;
else
{
++curwin->w_cursor.lnum;
curwin->w_cursor.col = 0;
curwin->w_set_curswant = TRUE;
op_inclusive = FALSE;
}
continue;
}
if (op_type == NOP)
beep_flush();
else
{
if (lineempty(curwin->w_cursor.lnum))
clearopbeep();
else
{
op_inclusive = TRUE;
if (n)
beep_flush();
}
}
break;
}
}
break;
case 'h':
case K_LEFT:
case K_BS:
case Ctrl('H'):
op_motion_type = MCHAR;
op_inclusive = FALSE;
n = Prenum1;
while (n--)
{
if (oneleft() == FAIL)
{
/* backspace and del wrap to previous line if 'whichwrap'
* bit 0 set.
* 'h' wraps to previous line if 'whichwrap' bit 2 set.
* CURS_LEFT wraps to previous line if 'whichwrap' bit 3
* set. */
if ( (((c == K_BS || c == Ctrl('H'))
&& vim_strchr(p_ww, 'b') != NULL) ||
(c == 'h' && vim_strchr(p_ww, 'h') != NULL) ||
(c == K_LEFT && vim_strchr(p_ww, '<') != NULL)) &&
curwin->w_cursor.lnum > 1)
{
--(curwin->w_cursor.lnum);
coladvance(MAXCOL);
curwin->w_set_curswant = TRUE;
/* When the NL before the first char has to be deleted we
* put the cursor on the NUL after the previous line.
* This is a very special case, be careful!
* don't adjust op_end now, otherwise it won't work */
if ((op_type == DELETE || op_type == CHANGE) &&
!lineempty(curwin->w_cursor.lnum))
{
++curwin->w_cursor.col;
dont_adjust_op_end = TRUE;
}
continue;
}
else if (op_type != DELETE && op_type != CHANGE)
beep_flush();
else if (Prenum1 == 1)
clearopbeep();
break;
}
}
break;
case '-':
flag = TRUE;
/* FALLTHROUGH */
case 'k':
case K_UP:
case Ctrl('P'):
normal_k:
op_motion_type = MLINE;
if (cursor_up(Prenum1) == FAIL)
clearopbeep();
else if (flag)
beginline(TRUE);
break;
case '+':
case CR:
flag = TRUE;
/* FALLTHROUGH */
case 'j':
case K_DOWN:
case Ctrl('N'):
case NL:
normal_j:
op_motion_type = MLINE;
if (cursor_down(Prenum1) == FAIL)
clearopbeep();
else if (flag)
beginline(TRUE);
break;
/*
* This is a strange motion command that helps make operators more
* logical. It is actually implemented, but not documented in the
* real 'vi'. This motion command actually refers to "the current
* line". Commands like "dd" and "yy" are really an alternate form of
* "d_" and "y_". It does accept a count, so "d3_" works to delete 3
* lines.
*/
case '_':
lineop:
old_col = curwin->w_curswant;
op_motion_type = MLINE;
if (cursor_down((long)(Prenum1 - 1)) == FAIL)
clearopbeep();
if (op_type == DELETE || op_type == LSHIFT || op_type == RSHIFT)
beginline(MAYBE);
else if (op_type != YANK) /* 'Y' does not move cursor */
beginline(TRUE);
break;
case K_HOME:
if ((mod_mask & MOD_MASK_CTRL))
goto goto_line_one;
Prenum = 1;
/* FALLTHROUGH */
case '|':
op_motion_type = MCHAR;
op_inclusive = FALSE;
beginline(FALSE);
if (Prenum > 0)
{
coladvance((colnr_t)(Prenum - 1));
curwin->w_curswant = (colnr_t)(Prenum - 1);
}
else
curwin->w_curswant = 0;
/* keep curswant at the column where we wanted to go, not where
we ended; differs is line is too short */
curwin->w_set_curswant = FALSE;
break;
/*
* Word Motions
*/
case 'B':
type = 1;
/* FALLTHROUGH */
case 'b':
case K_S_LEFT:
op_motion_type = MCHAR;
op_inclusive = FALSE;
curwin->w_set_curswant = TRUE;
if (bck_word(Prenum1, type, FALSE) == FAIL)
clearopbeep();
break;
case 'E':
type = 1;
/* FALLTHROUGH */
case 'e':
op_inclusive = TRUE;
goto dowrdcmd;
case 'W':
type = 1;
/* FALLTHROUGH */
case 'w':
case K_S_RIGHT:
op_inclusive = FALSE;
flag = TRUE;
/*
* This is a little strange. To match what the real vi does, we
* effectively map 'cw' to 'ce', and 'cW' to 'cE', provided that we
* are not on a space or a TAB. This seems impolite at first, but it's
* really more what we mean when we say 'cw'.
* Another strangeness: When standing on the end of a word "ce" will
* change until the end of the next wordt, but "cw" will change only
* one character! This is done by setting type to 2.
*/
if (op_type == CHANGE && (n = gchar_cursor()) != ' ' && n != TAB &&
n != NUL)
{
op_inclusive = TRUE;
flag = FALSE;
flag2 = TRUE;
}
dowrdcmd:
op_motion_type = MCHAR;
curwin->w_set_curswant = TRUE;
if (flag)
n = fwd_word(Prenum1, type, op_type != NOP);
else
n = end_word(Prenum1, type, flag2, FALSE);
if (n == FAIL)
clearopbeep();
break;
case K_END:
if ((mod_mask & MOD_MASK_CTRL))
goto goto_line;
/* FALLTHROUGH */
case '$':
op_motion_type = MCHAR;
op_inclusive = TRUE;
curwin->w_curswant = MAXCOL; /* so we stay at the end */
if (cursor_down((long)(Prenum1 - 1)) == FAIL)
{
clearopbeep();
break;
}
break;
case '^':
flag = TRUE;
/* FALLTHROUGH */
case '0':
op_motion_type = MCHAR;
op_inclusive = FALSE;
beginline(flag);
break;
/*
* 4: Searches
*/
case '?':
case '/':
if ((searchbuff = getcmdline(c, Prenum1)) == NULL)
{
clearop();
break;
}
op_motion_type = MCHAR;
op_inclusive = FALSE;
curwin->w_set_curswant = TRUE;
n = do_search(c, searchbuff, Prenum1,
SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
if (n == 0)
clearop();
else if (n == 2)
op_motion_type = MLINE;
break;
case 'N':
flag = SEARCH_REV;
case 'n':
op_motion_type = MCHAR;
op_inclusive = FALSE;
curwin->w_set_curswant = TRUE;
if (!do_search(0, NULL, Prenum1,
SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG | flag))
clearop();
break;
/*
* Character searches
*/
case 'T':
dir = BACKWARD;
/* FALLTHROUGH */
case 't':
type = 1;
goto docsearch;
case 'F':
dir = BACKWARD;
/* FALLTHROUGH */
case 'f':
docsearch:
op_motion_type = MCHAR;
if (dir == BACKWARD)
op_inclusive = FALSE;
else
op_inclusive = TRUE;
curwin->w_set_curswant = TRUE;
if (nchar >= 0x100 || !searchc(nchar, dir, type, Prenum1))
clearopbeep();
break;
case ',':
flag = 1;
/* FALLTHROUGH */
case ';':
dir = flag;
goto docsearch; /* nchar == NUL, thus repeat previous search */
/*
* section or C function searches
*/
case '[':
dir = BACKWARD;
/* FALLTHROUGH */
case ']':
op_motion_type = MCHAR;
op_inclusive = FALSE;
/*
* "[f" or "]f" : Edit file under the cursor (same as "gf")
*/
if (nchar == 'f')
goto gotofile;
/*
* Find the occurence(s) of the identifier or define under cursor
* in current and included files or jump to the first occurence.
*
* search list jump
* fwd bwd fwd bwd fwd bwd
* identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
* define "]d" "[d" "]D" "[D" "]^D" "[^D"
*/
if (nchar == 'i' || nchar == 'I' || nchar == Ctrl('I') ||
nchar == 'd' || nchar == 'D' || nchar == Ctrl('D'))
{
int len;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
{
clearop();
break;
}
find_pattern_in_path(ptr, len, TRUE,
Prenum == 0 ? !isupper(nchar) : FALSE,
((nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY,
Prenum1,
isupper(nchar) ? ACTION_SHOW_ALL :
islower(nchar) ? ACTION_SHOW : ACTION_GOTO,
c == ']' ? curwin->w_cursor.lnum : (linenr_t)1,
(linenr_t)MAXLNUM);
curwin->w_set_curswant = TRUE;
break;
}
/*
* "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
* "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
* "[/", "[*", "]/", "]*": go to Nth comment start/end.
*/
if ((c == '[' && vim_strchr((char_u *)"{(*/#", nchar) != NULL) ||
(c == ']' && vim_strchr((char_u *)"})*/#", nchar) != NULL))
{
FPOS old_pos;
FPOS new_pos;
if (nchar == '*')
nchar = '/';
old_pos = curwin->w_cursor;
new_pos.lnum = 0;
while (Prenum1--)
{
if ((pos = findmatchlimit(nchar,
(c == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
{
if (new_pos.lnum == 0) /* nothing found */
clearopbeep();
else
pos = &new_pos; /* use last one found */
break;
}
curwin->w_cursor = *pos;
new_pos= *pos;
}
curwin->w_cursor = old_pos;
if (pos != NULL)
{
setpcmark();
curwin->w_cursor = *pos;
curwin->w_set_curswant = TRUE;
}
break;
}
/*
* "[[", "[]", "]]" and "][": move to start or end of function
*/
if (nchar == '[' || nchar == ']')
{
if (nchar == c) /* "]]" or "[[" */
flag = '{';
else
flag = '}'; /* "][" or "[]" */
curwin->w_set_curswant = TRUE;
/*
* Imitate strange vi behaviour: When using "]]" with an operator
* we also stop at '}'.
*/
if (!findpar(dir, Prenum1, flag,
(op_type != NOP && dir == FORWARD && flag == '{')))
clearopbeep();
else if (op_type == NOP)
beginline(TRUE);
break;
}
/*
* "[p", "[P", "]P" and "]p": put with indent adjustment
*/
if (nchar == 'p' || nchar == 'P')
{
if (checkclearopq())
break;
prep_redo(Prenum, NUL, c, nchar, NUL);
do_put((c == ']' && nchar == 'p') ? FORWARD : BACKWARD,
Prenum1, TRUE);
break;
}
#ifdef USE_MOUSE
/*
* [ or ] followed by a middle mouse click: put selected text with
* indent adjustment. Any other button just does as usual.
*/
if (nchar >= K_LEFTMOUSE && nchar <= K_RIGHTRELEASE)
{
(void)do_mouse(nchar, (c == ']') ? FORWARD : BACKWARD,
Prenum1, TRUE);
break;
}
#endif /* USE_MOUSE */
/*
* end of '[' and ']': not a valid nchar
*/
clearopbeep();
break;
case '%':
op_inclusive = TRUE;
if (Prenum) /* {cnt}% : goto {cnt} percentage in file */
{
if (Prenum > 100)
clearopbeep();
else
{
op_motion_type = MLINE;
setpcmark();
/* round up, so CTRL-G will give same value */
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
Prenum + 99) / 100;
beginline(MAYBE);
}
}
else /* % : go to matching paren */
{
op_motion_type = MCHAR;
if ((pos = findmatch(NUL)) == NULL)
clearopbeep();
else
{
setpcmark();
curwin->w_cursor = *pos;
curwin->w_set_curswant = TRUE;
}
}
break;
case '(':
dir = BACKWARD;
/* FALLTHROUGH */
case ')':
op_motion_type = MCHAR;
if (c == ')')
op_inclusive = FALSE;
else
op_inclusive = TRUE;
curwin->w_set_curswant = TRUE;
if (findsent(dir, Prenum1) == FAIL)
clearopbeep();
break;
case '{':
dir = BACKWARD;
/* FALLTHROUGH */
case '}':
op_motion_type = MCHAR;
op_inclusive = FALSE;
curwin->w_set_curswant = TRUE;
if (!findpar(dir, Prenum1, NUL, FALSE))
clearopbeep();
break;
/*
* 5: Edits
*/
case '.': /* redo command */
if (checkclearopq())
break;
/*
* if restart_edit is TRUE, the last but one command is repeated
* instead of the last command (inserting text). This is used for
* CTRL-O <.> in insert mode
*/
if (start_redo(Prenum, restart_edit && !arrow_used) == FAIL)
clearopbeep();
break;
case 'u': /* undo */
if (VIsual_active || op_type == vim_strchr(opchars, 'u') - opchars + 1)
goto dooperator;
case K_UNDO:
if (checkclearopq())
break;
u_undo((int)Prenum1);
curwin->w_set_curswant = TRUE;
break;
case Ctrl('R'): /* undo undo */
if (checkclearopq())
break;
u_redo((int)Prenum1);
curwin->w_set_curswant = TRUE;
break;
case 'U': /* Undo line */
if (VIsual_active || op_type == vim_strchr(opchars, 'U') - opchars + 1)
goto dooperator;
if (checkclearopq())
break;
u_undoline();
curwin->w_set_curswant = TRUE;
break;
case 'r':
if (VIsual_active)
{
c = 'c';
goto dooperator;
}
if (checkclearop())
break;
ptr = ml_get_cursor();
/* special key or not enough characters to replace */
if (nchar >= 0x100 || STRLEN(ptr) < (unsigned)Prenum1)
{
clearopbeep();
break;
}
/*
* Replacing with a TAB is done by edit(), because it is complicated
* when 'expandtab' is set.
* Other characters are done below to avoid problems with things like
* CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
*/
if (nchar == '\t' && curbuf->b_p_et)
{
prep_redo(Prenum1, NUL, 'r', '\t', NUL);
stuffnumReadbuff(Prenum1);
stuffcharReadbuff('R');
stuffcharReadbuff('\t');
stuffcharReadbuff(ESC);
break;
}
if (nchar == Ctrl('V')) /* get another character */
{
c = Ctrl('V');
nchar = get_literal();
}
else
c = NUL;
prep_redo(Prenum1, NUL, 'r', c, nchar);
if (u_save_cursor() == FAIL) /* save line for undo */
break;
/*
* Replace characters by a newline.
* Strange vi behaviour: Only one newline is inserted.
* Delete the characters here.
* Insert the newline with an insert command, takes care of
* autoindent.
*/
if (c != Ctrl('V') && (nchar == '\r' || nchar == '\n'))
{
while (Prenum1--) /* delete the characters */
delchar(FALSE);
/* replacing the last character of a line is different */
if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL)
{
--curwin->w_cursor.col;
stuffcharReadbuff('a');
}
else
stuffcharReadbuff('i');
stuffcharReadbuff('\r');
stuffcharReadbuff(ESC);
}
else
{
while (Prenum1--) /* replace the characters */
{
/*
* Replace a 'normal' character.
* Get ptr again, because u_save and/or showmatch() will have
* released the line. At the same time we let know that the
* line will be changed.
*/
ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
ptr[curwin->w_cursor.col] = nchar;
if (p_sm && (nchar == ')' || nchar == '}' || nchar == ']'))
showmatch();
++curwin->w_cursor.col;
}
--curwin->w_cursor.col; /* cursor on the last replaced char */
}
curwin->w_set_curswant = TRUE;
CHANGED;
updateline();
set_last_insert(nchar);
break;
case 'J':
if (VIsual_active) /* join the visual lines */
goto dooperator;
if (checkclearop())
break;
if (Prenum <= 1)
Prenum = 2; /* default for join is two lines! */
if (curwin->w_cursor.lnum + Prenum - 1 > curbuf->b_ml.ml_line_count)
{
clearopbeep(); /* beyond last line */
break;
}
prep_redo(Prenum, NUL, 'J', NUL, NUL);
do_do_join(Prenum, TRUE, TRUE);
break;
case 'P':
dir = BACKWARD;
/* FALLTHROUGH */
case 'p':
/*
* 'P' after an operator or with Visual: Set current block.
* 'p' after an operator or with Visual: Set current paragraph.
*/
if (op_type != NOP || VIsual_active)
{
if (c == 'P')
{
if (current_block('{', Prenum1) == FAIL)
clearopbeep();
}
else
{
if (current_par(c, Prenum1) == FAIL)
clearopbeep();
}
curwin->w_set_curswant = TRUE;
}
else
{
prep_redo(Prenum, NUL, c, NUL, NUL);
do_put(dir, Prenum1, FALSE);
}
break;
case Ctrl('A'): /* add to number */
case Ctrl('X'): /* subtract from number */
if (checkclearopq())
break;
if (do_addsub((int)c, Prenum1) == OK)
prep_redo(Prenum1, NUL, c, NUL, NUL);
break;
/*
* 6: Inserts
*/
case 'A':
type = 1;
/* FALLTHROUGH */
case 'a':
if (op_type != NOP || VIsual_active)
{
if (current_word(Prenum1, type) == FAIL)
clearopbeep();
curwin->w_set_curswant = TRUE;
}
else
{
if (c == 'A')
{
curwin->w_set_curswant = TRUE;
while (oneright() == OK)
;
}
/* Works just like an 'i'nsert on the next character. */
if (u_save_cursor() == OK)
{
if (!lineempty(curwin->w_cursor.lnum))
inc_cursor();
command_busy = edit(c, FALSE, Prenum1);
}
}
break;
case 'I':
if (checkclearopq())
break;
beginline(TRUE);
/* FALLTHROUGH */
case 'i':
case K_INS:
insert_command:
if (checkclearopq())
break;
if (u_save_cursor() == OK)
command_busy = edit(c, FALSE, Prenum1);
break;
case 'o':
if (VIsual_active) /* switch start and end of visual */
{
Prenum = VIsual.lnum;
VIsual.lnum = curwin->w_cursor.lnum;
curwin->w_cursor.lnum = Prenum;
n = VIsual.col;
VIsual.col = curwin->w_cursor.col;
curwin->w_cursor.col = (int)n;
curwin->w_set_curswant = TRUE;
break;
}
if (checkclearop())
break;
if (has_format_option(FO_OPEN_COMS))
fo_do_comments = TRUE;
if (u_save(curwin->w_cursor.lnum,
(linenr_t)(curwin->w_cursor.lnum + 1)) == OK &&
Opencmd(FORWARD, TRUE, FALSE))
command_busy = edit('o', TRUE, Prenum1);
fo_do_comments = FALSE;
break;
case 'O':
if (checkclearopq())
break;
if (has_format_option(FO_OPEN_COMS))
fo_do_comments = TRUE;
if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
curwin->w_cursor.lnum) == OK && Opencmd(BACKWARD, TRUE, FALSE))
command_busy = edit('O', TRUE, Prenum1);
fo_do_comments = FALSE;
break;
case 'R':
if (VIsual_active)
{
c = 'c';
VIsual_mode = 'V';
goto dooperator;
}
if (checkclearopq())
break;
if (u_save_cursor() == OK)
command_busy = edit('R', FALSE, Prenum1);
break;
/*
* 7: Operators
*/
case '~': /* swap case */
/*
* if tilde is not an operator and Visual is off: swap case
* of a single character
*/
if (!p_to && !VIsual_active &&
op_type != vim_strchr(opchars, '~') - opchars + 1)
{
if (checkclearopq())
break;
if (lineempty(curwin->w_cursor.lnum))
{
clearopbeep();
break;
}
prep_redo(Prenum, NUL, '~', NUL, NUL);
if (u_save_cursor() == FAIL)
break;
for (; Prenum1 > 0; --Prenum1)
{
if (gchar_cursor() == NUL)
break;
swapchar(&curwin->w_cursor);
inc_cursor();
}
curwin->w_set_curswant = TRUE;
CHANGED;
updateline();
break;
}
/*FALLTHROUGH*/
case 'd':
case 'c':
case 'y':
case '>':
case '<':
case '!':
case '=':
case 'Q': /* should start Ex mode */
dooperator:
n = vim_strchr(opchars, c) - opchars + 1;
if (n == op_type) /* double operator works on lines */
goto lineop;
if (checkclearop())
break;
if (Prenum != 0)
opnum = Prenum;
curbuf->b_op_start = curwin->w_cursor;
op_type = (int)n;
break;
/*
* 8: Abbreviations
*/
/* when Visual the next commands are operators */
case K_DEL:
c = 'x'; /* DEL key behaves like 'x' */
case 'S':
case 'Y':
case 'D':
case 'C':
case 'x':
case 'X':
case 's':
/*
* 's' or 'S' with an operator: Operate on sentence or section.
*/
if (op_type != NOP || VIsual_active)
{
if (c == 's') /* sentence */
{
if (current_sent(Prenum1) == FAIL)
clearopbeep();
curwin->w_set_curswant = TRUE;
break;
}
if (c == 'S') /* block with () */
{
if (current_block('(', Prenum1) == FAIL)
clearopbeep();
curwin->w_set_curswant = TRUE;
break;
}
}
if (VIsual_active)
{
static char_u trans[] = "YyDdCcxdXd";
/* uppercase means linewise */
if (isupper(c) && VIsual_mode != Ctrl('V'))
VIsual_mode = 'V';
c = *(vim_strchr(trans, c) + 1);
goto dooperator;
}
case '&':
if (checkclearopq())
break;
if (Prenum)
stuffnumReadbuff(Prenum);
{
static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
(char_u *)"d$", (char_u *)"c$",
(char_u *)"cl", (char_u *)"cc",
(char_u *)"yy", (char_u *)":s\r"};
static char_u *str = (char_u *)"xXDCsSY&";
stuffReadbuff(ar[(int)(vim_strchr(str, c) - str)]);
}
break;
/*
* 9: Marks
*/
case 'm':
if (checkclearop())
break;
if (setmark(nchar) == FAIL)
clearopbeep();
break;
case '\'':
flag = TRUE;
/* FALLTHROUGH */
case '`':
pos = getmark(nchar, (op_type == NOP));
if (pos == (FPOS *)-1) /* jumped to other file */
{
if (flag)
beginline(TRUE);
break;
}
cursormark:
if (check_mark(pos) == FAIL)
clearop();
else
{
if (c == '\'' || c == '`')
setpcmark();
curwin->w_cursor = *pos;
if (flag)
beginline(TRUE);
}
op_motion_type = flag ? MLINE : MCHAR;
op_inclusive = FALSE; /* ignored if not MCHAR */
curwin->w_set_curswant = TRUE;
break;
case Ctrl('O'): /* goto older pcmark */
Prenum1 = -Prenum1;
/* FALLTHROUGH */
case Ctrl('I'): /* goto newer pcmark */
if (checkclearopq())
break;
pos = movemark((int)Prenum1);
if (pos == (FPOS *)-1) /* jump to other file */
{
curwin->w_set_curswant = TRUE;
break;
}
if (pos != NULL) /* can jump */
goto cursormark;
clearopbeep();
break;
/*
* 10. Buffer setting
*/
case '"':
if (checkclearop())
break;
if (nchar != NUL && is_yank_buffer(nchar, FALSE))
{
yankbuffer = nchar;
opnum = Prenum; /* remember count before '"' */
}
else
clearopbeep();
break;
/*
* 11. Visual
*/
case 'v':
case 'V':
case Ctrl('V'):
if (checkclearop())
break;
/* change Visual mode */
if (VIsual_active)
{
if (VIsual_mode == c) /* stop visual mode */
{
end_visual_mode();
}
else /* toggle char/block mode */
{ /* or char/line mode */
VIsual_mode = c;
showmode();
}
update_curbuf(NOT_VALID); /* update the inversion */
}
/* start Visual mode */
else
{
VIsual_save = VIsual; /* keep for "gv" */
VIsual_mode_save = VIsual_mode;
start_visual_highlight();
if (Prenum) /* use previously selected part */
{
if (resel_VIsual_mode == NUL) /* there is none */
{
beep_flush();
break;
}
VIsual = curwin->w_cursor;
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
redraw_cmdline = TRUE; /* show visual mode later */
/*
* For V and ^V, we multiply the number of lines even if there
* was only one -- webb
*/
if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
{
curwin->w_cursor.lnum += resel_VIsual_line_count * Prenum - 1;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
VIsual_mode = resel_VIsual_mode;
if (VIsual_mode == 'v')
{
if (resel_VIsual_line_count <= 1)
curwin->w_cursor.col += resel_VIsual_col * Prenum - 1;
else
curwin->w_cursor.col = resel_VIsual_col;
}
if (resel_VIsual_col == MAXCOL)
{
curwin->w_curswant = MAXCOL;
coladvance(MAXCOL);
}
else if (VIsual_mode == Ctrl('V'))
{
curwin->w_curswant = curwin->w_virtcol +
resel_VIsual_col * Prenum - 1;
coladvance((colnr_t)curwin->w_curswant);
}
else
curwin->w_set_curswant = TRUE;
curs_columns(TRUE); /* recompute w_virtcol */
update_curbuf(NOT_VALID); /* show the inversion */
}
else
{
VIsual = curwin->w_cursor;
VIsual_mode = c;
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
redraw_cmdline = TRUE; /* show visual mode later */
updateline(); /* start the inversion */
}
}
break;
/*
* 12. Suspend
*/
case Ctrl('Z'):
clearop();
if (VIsual_active)
end_visual_mode(); /* stop Visual */
stuffReadbuff((char_u *)":st\r"); /* with autowrite */
break;
/*
* 13. Window commands
*/
case Ctrl('W'):
if (checkclearop())
break;
do_window(nchar, Prenum); /* everything is in window.c */
break;
/*
* 14. extended commands (starting with 'g')
*/
case 'g':
switch (nchar)
{
/*
* "gv": reselect the previous visual area
*/
case 'v':
if (checkclearop())
break;
if (VIsual_active)
pos = &VIsual_save;
else
pos = &VIsual;
if (pos->lnum == 0 || pos->lnum > curbuf->b_ml.ml_line_count ||
VIsual_end.lnum == 0)
beep_flush();
else
{
FPOS tt;
int t;
/* exchange previous and current visual area */
if (VIsual_active)
{
tt = VIsual;
VIsual = VIsual_save;
VIsual_save = tt;
t = VIsual_mode;
VIsual_mode = VIsual_mode_save;
VIsual_mode_save = t;
tt = curwin->w_cursor;
}
curwin->w_cursor = VIsual_end;
if (VIsual_active)
VIsual_end = tt;
check_cursor();
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
update_curbuf(NOT_VALID);
showmode();
}
break;
/*
* "gj" and "gk" two new funny movement keys -- up and down
* movement based on *screen* line rather than *file* line.
*/
case 'j':
case K_DOWN:
if (!curwin->w_p_wrap)
goto normal_j;
if (screengo(FORWARD, Prenum1) == FAIL)
clearopbeep();
break;
case 'k':
case K_UP:
if (!curwin->w_p_wrap)
goto normal_k;
if (screengo(BACKWARD, Prenum1) == FAIL)
clearopbeep();
break;
/*
* "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
*/
case '^':
flag = TRUE;
/* FALLTHROUGH */
case '0':
case K_HOME:
op_motion_type = MCHAR;
op_inclusive = FALSE;
if (curwin->w_p_wrap)
{
n = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
Columns) * Columns;
if (curwin->w_p_nu && n > 8)
n -= 8;
}
else
n = curwin->w_leftcol;
coladvance((colnr_t)n);
if (flag)
while (vim_iswhite(gchar_cursor()) && oneright() == OK)
;
curwin->w_set_curswant = TRUE;
break;
case '$':
case K_END:
op_motion_type = MCHAR;
op_inclusive = TRUE;
if (curwin->w_p_wrap)
{
curwin->w_curswant = MAXCOL; /* so we stay at the end */
if (Prenum1 == 1)
{
n = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
Columns + 1) * Columns - 1;
if (curwin->w_p_nu && n > 8)
n -= 8;
coladvance((colnr_t)n);
}
else if (screengo(FORWARD, Prenum1 - 1) == FAIL)
clearopbeep();
}
else
{
n = curwin->w_leftcol + Columns - 1;
if (curwin->w_p_nu)
n -= 8;
coladvance((colnr_t)n);
curwin->w_set_curswant = TRUE;
}
break;
/*
* "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
*/
case '*':
case '#':
goto search_word;
/*
* ge and gE: go back to end of word
*/
case 'e':
case 'E':
op_motion_type = MCHAR;
curwin->w_set_curswant = TRUE;
op_inclusive = TRUE;
if (bckend_word(Prenum1, nchar == 'E', FALSE) == FAIL)
clearopbeep();
break;
/*
* g CTRL-G: display info about cursor position
*/
case Ctrl('G'):
cursor_pos_info();
break;
/*
* "gI": Start insert in column 1.
*/
case 'I':
beginline(FALSE);
goto insert_command;
/*
* "gf": goto file, edit file under cursor
* "]f" and "[f": can also be used.
*/
case 'f':
gotofile:
ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP);
if (ptr != NULL)
{
/* do autowrite if necessary */
if (curbuf->b_changed && curbuf->b_nwindows <= 1 && !p_hid)
autowrite(curbuf);
setpcmark();
(void)do_ecmd(0, ptr, NULL, NULL, p_hid, (linenr_t)0,
FALSE);
vim_free(ptr);
}
else
clearop();
break;
/*
* "gs": Goto sleep, but keep on checking for CTRL-C
*/
case 's':
while (Prenum1-- && !got_int)
{
mch_delay(1000L, TRUE);
mch_breakcheck();
}
break;
/*
* "ga": Display the ascii value of the character under the
* cursor. It is displayed in decimal, hex, and octal. -- webb
*/
case 'a':
do_ascii();
break;
/*
* "gg": Goto the first line in file. With a count it goes to
* that line number like for G. -- webb
*/
case 'g':
goto_line_one:
if (Prenum == 0)
Prenum = 1;
goto goto_line;
/*
* Operater to format text:
* gq same as 'Q' operator.
* Operators to change the case of text:
* g~ Toggle the case of the text.
* gu Change text to lower case.
* gU Change text to upper case.
* --webb
*/
case 'q':
case '~':
case 'u':
case 'U':
prechar = c;
c = nchar;
goto dooperator;
/*
* "gd": Find first occurence of pattern under the cursor in the
* current function
* "gD": idem, but in the current file.
*/
case 'd':
case 'D':
do_gd(nchar);
break;
#ifdef USE_MOUSE
/*
* g<*Mouse> : <C-*mouse>
*/
case K_MIDDLEMOUSE:
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
case K_LEFTMOUSE:
case K_LEFTDRAG:
case K_LEFTRELEASE:
case K_RIGHTMOUSE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
mod_mask = MOD_MASK_CTRL;
(void)do_mouse(nchar, BACKWARD, Prenum1, FALSE);
break;
case K_IGNORE:
break;
#endif
default:
clearopbeep();
break;
}
break;
/*
* 15. mouse click
*/
#ifdef USE_MOUSE
case K_MIDDLEMOUSE:
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
case K_LEFTMOUSE:
case K_LEFTDRAG:
case K_LEFTRELEASE:
case K_RIGHTMOUSE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
(void)do_mouse(c, BACKWARD, Prenum1, FALSE);
break;
case K_IGNORE:
break;
#endif
#ifdef USE_GUI
/*
* 16. scrollbar movement
*/
case K_SCROLLBAR:
if (op_type != NOP)
clearopbeep();
/* Even if an operator was pending, we still want to scroll */
gui_do_scroll();
break;
case K_HORIZ_SCROLLBAR:
if (op_type != NOP)
clearopbeep();
/* Even if an operator was pending, we still want to scroll */
gui_do_horiz_scroll();
break;
#endif
/*
* 17. The end
*/
case ESC:
/* Don't drop through and beep if we are canceling a command: */
if (!VIsual_active && (op_type != NOP ||
opnum || Prenum || yankbuffer))
{
clearop(); /* don't beep */
break;
}
if (VIsual_active)
{
end_visual_mode(); /* stop Visual */
update_curbuf(NOT_VALID);
clearop(); /* don't beep */
break;
}
/* ESC in normal mode: beep, but don't flush buffers */
clearop();
vim_beep();
break;
default: /* not a known command */
clearopbeep();
break;
} /* end of switch on command character */
/*
* if we didn't start or finish an operator, reset yankbuffer, unless we
* need it later.
*/
if (!finish_op && !op_type && vim_strchr((char_u *)"\"DCYSsXx.", c) == NULL)
yankbuffer = 0;
/*
* If an operation is pending, handle it...
*/
do_pending_operator(c, nchar, finish_op, searchbuff,
&command_busy, old_col, FALSE, dont_adjust_op_end);
normal_end:
if (op_type == NOP && yankbuffer == 0)
clear_showcmd();
if (restart_edit && op_type == NOP && !VIsual_active
&& !command_busy && stuff_empty() && yankbuffer == 0)
(void)edit(restart_edit, FALSE, 1L);
checkpcmark(); /* check if we moved since setting pcmark */
vim_free(searchbuff);
/*
* Update the other windows for the current buffer if modified has been set in
* set_Changed() (This should be done more efficiently)
*/
if (modified)
{
WIN *wp;
for (wp = firstwin; wp; wp = wp->w_next)
if (wp != curwin && wp->w_buffer == curbuf)
{
cursor_off();
wp->w_redr_type = NOT_VALID;
/*
* don't do the actual redraw if wait_return() has just been
* called and the user typed a ":"
*/
if (!skip_redraw)
win_update(wp);
}
modified = FALSE;
}
}
/*
* Handle an operator after visual mode or when the movement is finished
*/
void
do_pending_operator(c, nchar, finish_op, searchbuff, command_busy,
old_col, gui_yank, dont_adjust_op_end)
register int c;
int nchar;
int finish_op;
char_u *searchbuff;
int *command_busy;
int old_col;
int gui_yank; /* yanking visual area for GUI */
int dont_adjust_op_end;
{
/* The visual area is remembered for redo */
static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
static linenr_t redo_VIsual_line_count; /* number of lines */
static colnr_t redo_VIsual_col; /* number of cols or end column */
static long redo_VIsual_Prenum; /* Prenum for operator */
linenr_t Prenum1 = 1L;
FPOS old_cursor;
int VIsual_was_active = VIsual_active;
int redraw;
#ifdef USE_GUI
/*
* Yank the visual area into the GUI selection register before we operate
* on it and lose it forever. This could call do_pending_operator()
* recursively, but that's OK because gui_yank will be TRUE for the
* nested call. Note also that we call gui_copy_selection() and not
* gui_auto_select(). This is because even when 'autoselect' is not set,
* if we operate on the text, eg by deleting it, then this is considered to
* be an explicit request for it to be put in the global cut buffer, so we
* always want to do it here. -- webb
*/
if (gui.in_use && op_type != NOP && !gui_yank && VIsual_active
&& !redo_VIsual_busy)
gui_copy_selection();
#endif
old_cursor = curwin->w_cursor;
/*
* If an operation is pending, handle it...
*/
if ((VIsual_active || finish_op) && op_type != NOP)
{
op_is_VIsual = VIsual_active;
if (op_type != YANK && !VIsual_active) /* can't redo yank */
{
prep_redo(Prenum, prechar, opchars[op_type - 1], c, nchar);
if (c == '/' || c == '?') /* was a search */
{
/*
* If 'cpoptions' does not contain 'r', insert the search
* pattern to really repeat the same command.
*/
if (vim_strchr(p_cpo, CPO_REDO) == NULL)
AppendToRedobuff(searchbuff);
AppendToRedobuff(NL_STR);
}
}
if (redo_VIsual_busy)
{
curbuf->b_op_start = curwin->w_cursor;
curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
VIsual_mode = redo_VIsual_mode;
if (VIsual_mode == 'v')
{
if (redo_VIsual_line_count <= 1)
curwin->w_cursor.col += redo_VIsual_col - 1;
else
curwin->w_cursor.col = redo_VIsual_col;
}
if (redo_VIsual_col == MAXCOL)
{
curwin->w_curswant = MAXCOL;
coladvance(MAXCOL);
}
Prenum = redo_VIsual_Prenum;
}
else if (VIsual_active)
{
curbuf->b_op_start = VIsual;
VIsual_end = curwin->w_cursor;
if (VIsual_mode == 'V')
curbuf->b_op_start.col = 0;
}
if (lt(curbuf->b_op_start, curwin->w_cursor))
{
curbuf->b_op_end = curwin->w_cursor;
curwin->w_cursor = curbuf->b_op_start;
}
else
{
curbuf->b_op_end = curbuf->b_op_start;
curbuf->b_op_start = curwin->w_cursor;
}
op_line_count = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
if (VIsual_active || redo_VIsual_busy)
{
if (VIsual_mode == Ctrl('V')) /* block mode */
{
colnr_t start, end;
op_block_mode = TRUE;
getvcol(curwin, &(curbuf->b_op_start),
&op_start_vcol, NULL, &op_end_vcol);
if (!redo_VIsual_busy)
{
getvcol(curwin, &(curbuf->b_op_end), &start, NULL, &end);
if (start < op_start_vcol)
op_start_vcol = start;
if (end > op_end_vcol)
op_end_vcol = end;
}
/* if '$' was used, get op_end_vcol from longest line */
if (curwin->w_curswant == MAXCOL)
{
curwin->w_cursor.col = MAXCOL;
op_end_vcol = 0;
for (curwin->w_cursor.lnum = curbuf->b_op_start.lnum;
curwin->w_cursor.lnum <= curbuf->b_op_end.lnum;
++curwin->w_cursor.lnum)
{
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
if (end > op_end_vcol)
op_end_vcol = end;
}
curwin->w_cursor = curbuf->b_op_start;
}
else if (redo_VIsual_busy)
op_end_vcol = op_start_vcol + redo_VIsual_col - 1;
coladvance(op_start_vcol);
}
if (!redo_VIsual_busy)
{
/*
* Prepare to reselect and redo Visual: this is based on the
* size of the Visual text
*/
resel_VIsual_mode = VIsual_mode;
if (curwin->w_curswant == MAXCOL)
resel_VIsual_col = MAXCOL;
else if (VIsual_mode == Ctrl('V'))
resel_VIsual_col = op_end_vcol - op_start_vcol + 1;
else if (op_line_count > 1)
resel_VIsual_col = curbuf->b_op_end.col;
else
resel_VIsual_col = curbuf->b_op_end.col -
curbuf->b_op_start.col + 1;
resel_VIsual_line_count = op_line_count;
}
/* can't redo yank and : */
if (op_type != YANK && op_type != COLON)
{
prep_redo(0L, NUL, 'v', prechar, opchars[op_type - 1]);
redo_VIsual_mode = resel_VIsual_mode;
redo_VIsual_col = resel_VIsual_col;
redo_VIsual_line_count = resel_VIsual_line_count;
redo_VIsual_Prenum = Prenum;
}
/*
* Mincl defaults to TRUE.
* If op_end is on a NUL (empty line) op_inclusive becomes FALSE
* This makes "d}P" and "v}dP" work the same.
*/
op_inclusive = TRUE;
if (VIsual_mode == 'V')
op_motion_type = MLINE;
else
{
op_motion_type = MCHAR;
if (*ml_get_pos(&(curbuf->b_op_end)) == NUL)
op_inclusive = FALSE;
}
redo_VIsual_busy = FALSE;
/*
* Switch Visual off now, so screen updating does
* not show inverted text when the screen is redrawn.
* With YANK and sometimes with COLON and FILTER there is no screen
* redraw, so it is done here to remove the inverted part.
*/
if (!gui_yank)
{
VIsual_active = FALSE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
clear_cmdline = TRUE; /* unshow visual mode later */
if (op_type == YANK || op_type == COLON || op_type == FILTER)
update_curbuf(NOT_VALID);
}
/* set Prenum1 for LSHIFT and RSHIFT, e.g. "V3j2>" */
if (Prenum == 0)
Prenum1 = 1L;
else
Prenum1 = Prenum;
}
curwin->w_set_curswant = TRUE;
/* op_empty is set when start and end are the same */
op_empty = (op_motion_type == MCHAR && !op_inclusive &&
equal(curbuf->b_op_start, curbuf->b_op_end));
/*
* If the end of an operator is in column one while op_motion_type is
* MCHAR and op_inclusive is FALSE, we put op_end after the last character
* in the previous line. If op_start is on or before the first non-blank
* in the line, the operator becomes linewise (strange, but that's the way
* vi does it).
*/
if (op_motion_type == MCHAR && op_inclusive == FALSE &&
!dont_adjust_op_end && curbuf->b_op_end.col == 0 &&
op_line_count > 1)
{
op_end_adjusted = TRUE; /* remember that we did this */
--op_line_count;
--curbuf->b_op_end.lnum;
if (inindent(0))
op_motion_type = MLINE;
else
{
curbuf->b_op_end.col = STRLEN(ml_get(curbuf->b_op_end.lnum));
if (curbuf->b_op_end.col)
{
--curbuf->b_op_end.col;
op_inclusive = TRUE;
}
}
}
else
op_end_adjusted = FALSE;
switch (op_type)
{
case LSHIFT:
case RSHIFT:
do_shift(op_type, TRUE, (int)Prenum1);
break;
case JOIN:
if (op_line_count < 2)
op_line_count = 2;
if (curwin->w_cursor.lnum + op_line_count - 1 >
curbuf->b_ml.ml_line_count)
beep_flush();
else
{
/*
* If the cursor position has been changed, recompute the
* current cursor position in the window. If it's not visible,
* don't keep the window updated when joining the lines.
*/
if (old_cursor.lnum != curwin->w_cursor.lnum ||
old_cursor.col != curwin->w_cursor.col)
redraw = (curs_rows() == OK);
else
redraw = TRUE;
do_do_join(op_line_count, TRUE, redraw);
}
break;
case DELETE:
if (!op_empty)
do_delete();
break;
case YANK:
if (!op_empty)
(void)do_yank(FALSE, !gui_yank);
break;
case CHANGE:
*command_busy = do_change(); /* will set op_type to NOP */
break;
case FILTER:
if (vim_strchr(p_cpo, CPO_FILTER) != NULL)
AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */
else
bangredo = TRUE; /* do_bang() will put cmd in redo buffer */
case INDENT:
case COLON:
#if defined(LISPINDENT) || defined(CINDENT)
/*
* If 'equalprg' is empty, do the indenting internally.
*/
if (op_type == INDENT && *p_ep == NUL)
{
# ifdef LISPINDENT
if (curbuf->b_p_lisp)
{
do_reindent(get_lisp_indent);
break;
}
# endif
# ifdef CINDENT
do_reindent(get_c_indent);
break;
# endif
}
#endif /* defined(LISPINDENT) || defined(CINDENT) */
dofilter:
if (VIsual_was_active)
sprintf((char *)IObuff, ":'<,'>");
else
sprintf((char *)IObuff, ":%ld,%ld",
(long)curbuf->b_op_start.lnum,
(long)curbuf->b_op_end.lnum);
stuffReadbuff(IObuff);
if (op_type != COLON)
stuffReadbuff((char_u *)"!");
if (op_type == INDENT)
{
#ifndef CINDENT
if (*p_ep == NUL)
stuffReadbuff((char_u *)"indent");
else
#endif
stuffReadbuff(p_ep);
stuffReadbuff((char_u *)"\n");
}
else if (op_type == FORMAT || op_type == GFORMAT)
{
if (*p_fp == NUL)
stuffReadbuff((char_u *)"fmt");
else
stuffReadbuff(p_fp);
stuffReadbuff((char_u *)"\n");
}
/* do_cmdline() does the rest */
break;
case TILDE:
case UPPER:
case LOWER:
if (!op_empty)
do_tilde();
break;
case FORMAT:
case GFORMAT:
if (*p_fp != NUL)
goto dofilter; /* use external command */
do_format(); /* use internal function */
break;
default:
clearopbeep();
}
prechar = NUL;
if (!gui_yank)
{
/*
* if 'sol' not set, go back to old column for some commands
*/
if (!p_sol && op_motion_type == MLINE && (op_type == LSHIFT ||
op_type == RSHIFT || op_type == DELETE))
coladvance(curwin->w_curswant = old_col);
op_type = NOP;
}
else
curwin->w_cursor = old_cursor;
op_block_mode = FALSE;
yankbuffer = 0;
}
}
#ifdef USE_MOUSE
/*
* Do the appropriate action for the current mouse click in the current mode.
*
* Normal Mode:
* event modi- position visual change action
* fier cursor window
* left press - yes end yes
* left press C yes end yes "^]" (2)
* left press S yes end yes "*" (2)
* left drag - yes start if moved no
* left relse - yes start if moved no
* middle press - yes if not active no put register
* middle press - yes if active no yank and put
* right press - yes start or extend yes
* right press S yes no change yes "#" (2)
* right drag - yes extend no
* right relse - yes extend no
*
* Insert or Replace Mode:
* event modi- position visual change action
* fier cursor window
* left press - yes (cannot be active) yes
* left press C yes (cannot be active) yes "CTRL-O^]" (2)
* left press S yes (cannot be active) yes "CTRL-O*" (2)
* left drag - yes start or extend (1) no CTRL-O (1)
* left relse - yes start or extend (1) no CTRL-O (1)
* middle press - no (cannot be active) no put register
* right press - yes start or extend yes CTRL-O
* right press S yes (cannot be active) yes "CTRL-O#" (2)
*
* (1) only if mouse pointer moved since press
* (2) only if click is in same buffer
*
* Return TRUE if start_arrow() should be called for edit mode.
*/
int
do_mouse(c, dir, count, fix_indent)
int c; /* K_LEFTMOUSE, etc */
int dir; /* Direction to 'put' if necessary */
long count;
int fix_indent; /* Do we fix indent for 'put' if necessary? */
{
static int ignore_drag_release = FALSE;
static FPOS orig_cursor;
static int do_always = FALSE; /* ignore 'mouse' setting next time */
static int got_click = FALSE; /* got a click some time back */
int which_button; /* MOUSE_LEFT, _MIDDLE or _RIGHT */
int is_click; /* If FALSE it's a drag or release event */
int is_drag; /* If TRUE it's a drag event */
int jump_flags = 0; /* flags for jump_to_mouse() */
FPOS start_visual;
FPOS end_visual;
BUF *save_buffer;
int diff;
int moved; /* Has cursor moved? */
int c1, c2;
int VIsual_was_active = VIsual_active;
/*
* When GUI is active, always recognize mouse events, otherwise:
* - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
* - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
* - For command line and insert mode 'mouse' is checked before calling
* do_mouse().
*/
if (do_always)
do_always = FALSE;
else
#ifdef USE_GUI
if (!gui.in_use)
#endif
{
if (VIsual_active)
{
if (!mouse_has(MOUSE_VISUAL))
return FALSE;
}
else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
return FALSE;
}
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
/*
* Ignore drag and release events if we didn't get a click.
*/
if (is_click)
got_click = TRUE;
else
{
if (!got_click) /* didn't get click, ignore */
return FALSE;
if (!is_drag) /* release, reset got_click */
got_click = FALSE;
}
/*
* ALT is currently ignored
*/
if ((mod_mask & MOD_MASK_ALT))
return FALSE;
/*
* CTRL right mouse button does CTRL-T
*/
if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
{
if (State & INSERT)
stuffcharReadbuff(Ctrl('O'));
stuffcharReadbuff(Ctrl('T'));
got_click = FALSE; /* ignore drag&release now */
return FALSE;
}
/*
* CTRL only works with left mouse button
*/
if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
return FALSE;
/*
* When a modifier is down, ignore drag and release events, as well as
* multiple clicks and the middle mouse button.
*/
if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) &&
(!is_click || (mod_mask & MOD_MASK_MULTI_CLICK) ||
which_button == MOUSE_MIDDLE))
return FALSE;
/*
* If the button press was used as the movement command for an operator
* (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
* drag/release events.
*/
if (!is_click && (ignore_drag_release || which_button == MOUSE_MIDDLE))
return FALSE;
/*
* Middle mouse button does a 'put' of the selected text
*/
if (which_button == MOUSE_MIDDLE)
{
if (State == NORMAL)
{
/*
* If an operator was pending, we don't know what the user wanted
* to do. Go back to normal mode: Clear the operator and beep().
*/
if (op_type != NOP)
{
clearopbeep();
return FALSE;
}
/*
* If visual was active, yank the highlighted text and put it
* before the mouse pointer position.
*/
if (VIsual_active)
{
stuffcharReadbuff('y');
stuffcharReadbuff(K_MIDDLEMOUSE);
do_always = TRUE; /* ignore 'mouse' setting next time */
return FALSE;
}
/*
* The rest is below jump_to_mouse()
*/
}
/*
* Middle click in insert mode doesn't move the mouse, just insert the
* contents of a register. '.' register is special, can't insert that
* with do_put().
*/
else if (State & INSERT)
{
if (yankbuffer == '.')
insertbuf(yankbuffer);
else
{
#ifdef USE_GUI
if (gui.in_use && yankbuffer == 0)
yankbuffer = '*';
#endif
do_put(BACKWARD, 1L, fix_indent);
/* Put cursor after the end of the just pasted text. */
curwin->w_cursor = curbuf->b_op_end;
if (gchar_cursor() != NUL)
++curwin->w_cursor.col;
/* Repeat it with CTRL-R x, not exactly the same, but mostly
* works fine. */
AppendCharToRedobuff(Ctrl('R'));
if (yankbuffer == 0)
AppendCharToRedobuff('"');
else
AppendCharToRedobuff(yankbuffer);
}
return FALSE;
}
else
return FALSE;
}
if (!is_click)
jump_flags |= MOUSE_FOCUS;
start_visual.lnum = 0;
if ((State & (NORMAL | INSERT)) &&
!(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
{
if (which_button == MOUSE_LEFT)
{
if (is_click)
{
if (VIsual_active)
{
end_visual_mode();
update_curbuf(NOT_VALID);
}
}
else
jump_flags |= MOUSE_MAY_VIS;
}
else if (which_button == MOUSE_RIGHT)
{
if (is_click && VIsual_active)
{
/*
* Remember the start and end of visual before moving the
* cursor.
*/
if (lt(curwin->w_cursor, VIsual))
{
start_visual = curwin->w_cursor;
end_visual = VIsual;
}
else
{
start_visual = VIsual;
end_visual = curwin->w_cursor;
}
}
jump_flags |= MOUSE_MAY_VIS;
}
}
if (!is_drag)
{
/*
* If an operator is pending, ignore all drags and releases until the
* next mouse click.
*/
ignore_drag_release = (op_type != NOP);
}
/*
* Jump!
*/
if (!is_click)
jump_flags |= MOUSE_DID_MOVE;
save_buffer = curbuf;
moved = (jump_to_mouse(jump_flags) & CURSOR_MOVED);
/* When jumping to another buffer, stop visual mode */
if (curbuf != save_buffer && VIsual_active)
{
end_visual_mode();
update_curbuf(NOT_VALID); /* delete the inversion */
}
else if (start_visual.lnum) /* right click in visual mode */
{
/*
* If the click is before the start of visual, change the start. If
* the click is after the end of visual, change the end. If the click
* is inside the visual, change the closest side.
*/
if (lt(curwin->w_cursor, start_visual))
VIsual = end_visual;
else if (lt(end_visual, curwin->w_cursor))
VIsual = start_visual;
else
{
/* In the same line, compare column number */
if (end_visual.lnum == start_visual.lnum)
{
if (curwin->w_cursor.col - start_visual.col >
end_visual.col - curwin->w_cursor.col)
VIsual = start_visual;
else
VIsual = end_visual;
}
/* In different lines, compare line number */
else
{
diff = (curwin->w_cursor.lnum - start_visual.lnum) -
(end_visual.lnum - curwin->w_cursor.lnum);
if (diff > 0) /* closest to end */
VIsual = start_visual;
else if (diff < 0) /* closest to start */
VIsual = end_visual;
else /* in the middle line */
{
if (curwin->w_cursor.col <
(start_visual.col + end_visual.col) / 2)
VIsual = end_visual;
else
VIsual = start_visual;
}
}
}
}
/*
* If Visual mode started in insert mode, execute "CTRL-O"
*/
else if ((State & INSERT) && VIsual_active)
stuffcharReadbuff(Ctrl('O'));
/*
* If cursor has moved, need to update Cline_row
*/
else if (moved)
cursupdate();
/*
* Middle mouse click: Put text before cursor.
*/
if (which_button == MOUSE_MIDDLE)
{
#ifdef USE_GUI
if (gui.in_use && yankbuffer == 0)
yankbuffer = '*';
#endif
if (yank_buffer_mline())
{
if (mouse_past_bottom)
dir = FORWARD;
}
else if (mouse_past_eol)
dir = FORWARD;
if (fix_indent)
{
c1 = (dir == BACKWARD) ? '[' : ']';
c2 = 'p';
}
else
{
c1 = (dir == FORWARD) ? 'p' : 'P';
c2 = NUL;
}
prep_redo(Prenum, NUL, c1, c2, NUL);
/*
* Remember where the paste started, so in edit() Insstart can be set
* to this position
*/
if (restart_edit)
where_paste_started = curwin->w_cursor;
do_put(dir, count, fix_indent);
/* Put cursor at the end of the just pasted text. */
curwin->w_cursor = curbuf->b_op_end;
if (restart_edit && gchar_cursor() != NUL)
++curwin->w_cursor.col; /* put cursor after the text */
}
/*
* Ctrl-Mouse click jumps to the tag under the mouse pointer
*/
else if ((mod_mask & MOD_MASK_CTRL))
{
if (State & INSERT)
stuffcharReadbuff(Ctrl('O'));
stuffcharReadbuff(Ctrl(']'));
ignore_drag_release = TRUE; /* ignore drag and release now */
}
/*
* Shift-Mouse click searches for the next occurrence of the word under
* the mouse pointer
*/
else if ((mod_mask & MOD_MASK_SHIFT))
{
if (State & INSERT)
stuffcharReadbuff(Ctrl('O'));
if (which_button == MOUSE_LEFT)
stuffcharReadbuff('*');
else /* MOUSE_RIGHT */
stuffcharReadbuff('#');
}
/* Handle double clicks */
else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT)))
{
if (is_click || !VIsual_active)
{
if (VIsual_active)
orig_cursor = VIsual;
else
{
start_visual_highlight();
VIsual = curwin->w_cursor;
orig_cursor = VIsual;
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
redraw_cmdline = TRUE; /* show visual mode later */
}
if (mod_mask & MOD_MASK_2CLICK)
VIsual_mode = 'v';
else if (mod_mask & MOD_MASK_3CLICK)
VIsual_mode = 'V';
else if (mod_mask & MOD_MASK_4CLICK)
VIsual_mode = Ctrl('V');
}
if (mod_mask & MOD_MASK_2CLICK)
{
if (lt(curwin->w_cursor, orig_cursor))
{
find_start_of_word(&curwin->w_cursor);
find_end_of_word(&VIsual);
}
else
{
find_start_of_word(&VIsual);
find_end_of_word(&curwin->w_cursor);
}
curwin->w_set_curswant = TRUE;
}
if (is_click)
{
curs_columns(TRUE); /* recompute w_virtcol */
update_curbuf(NOT_VALID); /* update the inversion */
}
}
else if (VIsual_active && VIsual_was_active != VIsual_active)
VIsual_mode = 'v';
return moved;
}
static void
find_start_of_word(pos)
FPOS *pos;
{
char_u *ptr;
int cclass;
ptr = ml_get(pos->lnum);
cclass = get_mouse_class(ptr[pos->col]);
/* Can't test pos->col >= 0 because pos->col is unsigned */
while (pos->col > 0 && get_mouse_class(ptr[pos->col]) == cclass)
pos->col--;
if (pos->col != 0 || get_mouse_class(ptr[0]) != cclass)
pos->col++;
}
static void
find_end_of_word(pos)
FPOS *pos;
{
char_u *ptr;
int cclass;
ptr = ml_get(pos->lnum);
cclass = get_mouse_class(ptr[pos->col]);
while (ptr[pos->col] && get_mouse_class(ptr[pos->col]) == cclass)
pos->col++;
pos->col--;
}
static int
get_mouse_class(c)
int c;
{
if (c == ' ' || c == '\t')
return ' ';
if (isidchar(c))
return 'a';
/*
* There are a few special cases where we want certain combinations of
* characters to be considered as a single word. These are things like
* "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
* character is in it's own class.
*/
if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
return '=';
return c;
}
#endif /* USE_MOUSE */
/*
* start highlighting for visual mode
*/
void
start_visual_highlight()
{
static int didwarn = FALSE; /* warned for broken inversion */
if (!didwarn && set_highlight('v') == FAIL)/* cannot highlight */
{
EMSG("Warning: terminal cannot highlight");
didwarn = TRUE;
}
}
/*
* End visual mode. If we are using the GUI, and autoselect is set, then
* remember what was selected in case we need to paste it somewhere while we
* still own the selection. This function should ALWAYS be called to end
* visual mode.
*/
void
end_visual_mode()
{
#ifdef USE_GUI
if (gui.in_use)
gui_auto_select();
#endif
VIsual_active = FALSE;
#ifdef USE_MOUSE
setmouse();
#endif
VIsual_end = curwin->w_cursor; /* remember for '> mark */
if (p_smd)
clear_cmdline = TRUE; /* unshow visual mode later */
}
/*
* Find the identifier under or to the right of the cursor. If none is
* found and find_type has FIND_STRING, then find any non-white string. The
* length of the string is returned, or zero if no string is found. If a
* string is found, a pointer to the string is put in *string, but note that
* the caller must use the length returned as this string may not be NUL
* terminated.
*/
int
find_ident_under_cursor(string, find_type)
char_u **string;
int find_type;
{
char_u *ptr;
int col = 0; /* init to shut up GCC */
int i;
/*
* if i == 0: try to find an identifier
* if i == 1: try to find any string
*/
ptr = ml_get_curline();
for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i)
{
/*
* skip to start of identifier/string
*/
col = curwin->w_cursor.col;
while (ptr[col] != NUL &&
(i == 0 ? !iswordchar(ptr[col]) : vim_iswhite(ptr[col])))
++col;
/*
* Back up to start of identifier/string. This doesn't match the
* real vi but I like it a little better and it shouldn't bother
* anyone.
* When FIND_IDENT isn't defined, we backup until a blank.
*/
while (col > 0 && (i == 0 ? iswordchar(ptr[col - 1]) :
(!vim_iswhite(ptr[col - 1]) &&
(!(find_type & FIND_IDENT) || !iswordchar(ptr[col - 1])))))
--col;
/*
* if we don't want just any old string, or we've found an identifier,
* stop searching.
*/
if (!(find_type & FIND_STRING) || iswordchar(ptr[col]))
break;
}
/*
* didn't find an identifier or string
*/
if (ptr[col] == NUL || (!iswordchar(ptr[col]) && i == 0))
{
if (find_type & FIND_STRING)
EMSG("No string under cursor");
else
EMSG("No identifier under cursor");
return 0;
}
ptr += col;
*string = ptr;
col = 0;
while (i == 0 ? iswordchar(*ptr) : (*ptr != NUL && !vim_iswhite(*ptr)))
{
++ptr;
++col;
}
return col;
}
static void
prep_redo(num, pre_char, cmd, c, nchar)
long num;
int pre_char;
int cmd;
int c;
int nchar;
{
ResetRedobuff();
if (yankbuffer != 0) /* yank from specified buffer */
{
AppendCharToRedobuff('\"');
AppendCharToRedobuff(yankbuffer);
}
if (num)
AppendNumberToRedobuff(num);
if (pre_char != NUL)
AppendCharToRedobuff(pre_char);
AppendCharToRedobuff(cmd);
if (c != NUL)
AppendCharToRedobuff(c);
if (nchar != NUL)
AppendCharToRedobuff(nchar);
}
/*
* check for operator active and clear it
*
* return TRUE if operator was active
*/
static int
checkclearop()
{
if (op_type == NOP)
return (FALSE);
clearopbeep();
return (TRUE);
}
/*
* check for operator or Visual active and clear it
*
* return TRUE if operator was active
*/
static int
checkclearopq()
{
if (op_type == NOP && !VIsual_active)
return (FALSE);
clearopbeep();
return (TRUE);
}
static void
clearop()
{
op_type = NOP;
yankbuffer = 0;
prechar = NUL;
}
static void
clearopbeep()
{
clearop();
beep_flush();
}
/*
* Routines for displaying a partly typed command
*/
static char_u showcmd_buf[SHOWCMD_COLS + 1];
static char_u old_showcmd_buf[SHOWCMD_COLS + 1]; /* For push_showcmd() */
static int is_showcmd_clear = TRUE;
static void display_showcmd __ARGS((void));
void
clear_showcmd()
{
if (!p_sc)
return;
showcmd_buf[0] = NUL;
/*
* Don't actually display something if there is nothing to clear.
*/
if (is_showcmd_clear)
return;
display_showcmd();
}
/*
* Add 'c' to string of shown command chars.
* Return TRUE if setcursor() has been called.
*/
int
add_to_showcmd(c, display_always)
int c;
int display_always;
{
char_u *p;
int old_len;
int extra_len;
int overflow;
if (!p_sc)
return FALSE;
p = transchar(c);
old_len = STRLEN(showcmd_buf);
extra_len = STRLEN(p);
overflow = old_len + extra_len - SHOWCMD_COLS;
if (overflow > 0)
STRCPY(showcmd_buf, showcmd_buf + overflow);
STRCAT(showcmd_buf, p);
if (!display_always && char_avail())
return FALSE;
display_showcmd();
return TRUE;
}
/*
* Delete 'len' characters from the end of the shown command.
*/
static void
del_from_showcmd(len)
int len;
{
int old_len;
if (!p_sc)
return;
old_len = STRLEN(showcmd_buf);
if (len > old_len)
len = old_len;
showcmd_buf[old_len - len] = NUL;
if (!char_avail())
display_showcmd();
}
void
push_showcmd()
{
if (p_sc)
STRCPY(old_showcmd_buf, showcmd_buf);
}
void
pop_showcmd()
{
if (!p_sc)
return;
STRCPY(showcmd_buf, old_showcmd_buf);
display_showcmd();
}
static void
display_showcmd()
{
int len;
cursor_off();
len = STRLEN(showcmd_buf);
if (len == 0)
is_showcmd_clear = TRUE;
else
{
screen_msg(showcmd_buf, (int)Rows - 1, sc_col);
is_showcmd_clear = FALSE;
}
/*
* clear the rest of an old message by outputing up to SHOWCMD_COLS spaces
*/
screen_msg((char_u *)" " + len, (int)Rows - 1, sc_col + len);
setcursor(); /* put cursor back where it belongs */
}
/*
* Implementation of "gd" and "gD" command.
*/
static void
do_gd(nchar)
int nchar;
{
int len;
char_u *pat;
FPOS old_pos;
int t;
int save_p_ws;
int save_p_scs;
char_u *ptr;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0 ||
(pat = alloc(len + 5)) == NULL)
{
clearopbeep();
return;
}
sprintf((char *)pat, iswordchar(*ptr) ? "\\<%.*s\\>" :
"%.*s", len, ptr);
old_pos = curwin->w_cursor;
save_p_ws = p_ws;
save_p_scs = p_scs;
p_ws = FALSE; /* don't wrap around end of file now */
p_scs = FALSE; /* don't switch ignorecase off now */
fo_do_comments = TRUE;
/*
* Search back for the end of the previous function.
* If this fails, and with "gD", go to line 1.
* Search forward for the identifier, ignore comment lines.
*/
if (nchar == 'D' || !findpar(BACKWARD, 1L, '}', FALSE))
{
setpcmark(); /* Set in findpar() otherwise */
curwin->w_cursor.lnum = 1;
}
while ((t = searchit(&curwin->w_cursor, FORWARD, pat, 1L, 0, RE_LAST))
== OK &&
get_leader_len(ml_get_curline(), NULL) &&
old_pos.lnum > curwin->w_cursor.lnum)
++curwin->w_cursor.lnum;
if (t == FAIL || old_pos.lnum <= curwin->w_cursor.lnum)
{
clearopbeep();
curwin->w_cursor = old_pos;
}
else
curwin->w_set_curswant = TRUE;
vim_free(pat);
p_ws = save_p_ws;
p_scs = save_p_scs;
fo_do_comments = FALSE;
}